#include "definitions.h"
#include "random.h"
#include "experiment.hpp"

#include <fstream>
#include <iostream>
#include <iomanip>
#include <limits>
using namespace std;

#define N_EXP_DEF 10
#define ERROR_DEF 0.1f
#define SEED 12345

//Net 1
//#define REF_VALUE 11.850029429075927f //3 jobs
//#define REF_VALUE 12.173507720894258f //5 jobs
//#define REF_VALUE 13.508085702541143f //10 jobs
//#define REF_VALUE 16.110872397521614f //15 jobs
//#define REF_VALUE 20.195217519840753f //20 jobs

//Net 2
//#define REF_VALUE 11829.178f //3 jobs

//checking
bool err_error(double x){
  return x < 0.01 || x > 1;
}

bool err_n_experiments(int x) {
  return x < 0;
}

void print_error(const char opt) {
  cerr << "ERROR in argument: " << opt << endl;
}

void print_help(string cmd) {
  cout << "Usage: " << cmd << " [options]" << endl
       << "Allowed options:" << endl
       << "-h\t\tthis help message" << endl
       << "-e arg\t\tmax. relative error" << endl
       << "-n arg\t\tn. of experiments" << endl;
}

//parsing
void get_arguments(char *argv[], int argc, arguments &args) {

  //default
  args.error = ERROR_DEF;
  args.n_experiments = N_EXP_DEF;
  args.seed = SEED;

  const char *options = "e:n:s:h";
  int opt;
  opterr = 0;

  while((opt = getopt(argc, argv, options)) != -1) {
    switch(opt) {
    case 'e':
      args.error = atof(optarg) / 100;
      if(err_error(args.error)) {
	print_error(opt);
	print_help(string(argv[0]));
	exit(1);
      }
      break;

    case 'n':
      args.n_experiments = atoi(optarg);
      if(err_n_experiments(args.n_experiments)) {
	print_error(opt);
	print_help(string(argv[0]));
	exit(1);
      }
      break;

    case 's':
      args.seed = atoi(optarg);
      break;

    case 'h': //help
      print_help(string(argv[0]));
      exit(0);

    default:
      print_help(string(argv[0]));
      exit(0);
    }
  }
}



int main(int argc, char *argv[]) {
  cout << "*** This is My Simulator" << endl;
  arguments args;
  get_arguments(argv, argc, args);

  cout << "n. jobs: " << N_JOBS << endl
       << "global seed: " << args.seed << endl
       << "max. relative error: " << args.error << " %" << endl
       << "confidence level: " << 100 - PRECISION << endl
       << "n. experiments: " << args.n_experiments << endl;

  //create the random generator for seeds
  rngb_t rng_(args.seed);
  rngint_t rng(rng_, gm_uniform_int_t(0, numeric_limits<int>::max()));

  unsigned int n_covering = 0, n_left = 0, n_right = 0;
  double reg_cycles = 0, cycles_length = 0;

  ofstream means("means.dat", ios_base::app);

  for(int i=0; i<args.n_experiments; ++i) {
    cout << "*** EXPERIMENT #" << i+1 << endl;
    interval_t output;
    cout << "generating seed... " << flush;
    int seed = rng();
    cout << seed << endl;
    cout << "> starting experiment" << endl;
    experiment(output, seed, args.error);
    cout << "cycle time " << fixed << setprecision(2) << output.confidence*100 << "%"
	 << fixed << setprecision(PRECISION)
	 << " interval: " << output.point << " -+ " << output.offset
	 << ", thickness: " << output.thickness*100 << "%" << endl;
    /*
    double m = output.point - output.offset;
    double M = output.point + output.offset;
    if(m <= REF_VALUE && REF_VALUE <= M)
      ++n_covering;
    else
      if(REF_VALUE < m)
	++n_left;
      else
	++n_right;
    */
    reg_cycles += output.n_reg_cycles;
    cycles_length += output.events_per_cycle;
    cout << "---\n";
    means << output.point << endl;
  }

  /*
  reg_cycles /= args.n_experiments;
  cycles_length /= args.n_experiments;
  float percent = 100.0f * (float)n_covering / args.n_experiments;

  cerr << percent << "\t" << cycles_length << "\t" << reg_cycles << endl;
  */
  
  cout << "> done" << endl;
  return 0;
}
